dragsource: Use double coordinates for checking drag threshold
authorAlexander Mikhaylenko <alexm@gnome.org>
Fri, 25 Dec 2020 17:57:48 +0000 (22:57 +0500)
committerAlexander Mikhaylenko <alexm@gnome.org>
Fri, 29 Jan 2021 07:01:34 +0000 (12:01 +0500)
If multiple nested widgets have drag sources on them, both using bubble
phase, we need to reliably pick the inner one. Both of them will try to
start dragging, and we need to make sure there are no situations where the
outer widget starts drag earlier and cancels the inner one.

Currently, this can easily happen via integer rounding: start and current
coordinates passed into gtk_drag_check_threshold() are initially doubles
(other than in GtkNotebook and GtkIconView), and are casted to ints. Then
those rounded values are used to calculate deltas to compare to the drag
threshold, losing quite a lot of precision along the way, and often
resulting in the outer widget getting larger deltas.

To avoid it, just don't round it. Introduce a variant of the function that
operates on doubles: gtk_drag_check_threshold_double() and use it instead
of the original everywhere.

17 files changed:
gtk/gtkcolumnview.c
gtk/gtkdragsource.c
gtk/gtkdragsourceprivate.h [new file with mode: 0644]
gtk/gtkentry.c
gtk/gtkgesturelongpress.c
gtk/gtkiconview.c
gtk/gtkiconviewprivate.h
gtk/gtkimcontextwayland.c
gtk/gtklabel.c
gtk/gtklistbase.c
gtk/gtknotebook.c
gtk/gtkplacessidebar.c
gtk/gtkscrolledwindow.c
gtk/gtktext.c
gtk/gtktextview.c
gtk/gtktreeview.c
gtk/gtktreeviewcolumn.c

index f00bede003caffa871d66237537d5ef070782ff9..2e835d366aca1e41c24c080b99b7794bac3853ae 100644 (file)
@@ -39,7 +39,7 @@
 #include "gtkadjustment.h"
 #include "gtkgesturedrag.h"
 #include "gtkeventcontrollermotion.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkeventcontrollerkey.h"
 #include "gtkgestureclick.h"
 
@@ -1128,7 +1128,7 @@ header_drag_update (GtkGestureDrag *gesture,
 
   if (!self->in_column_resize && !self->in_column_reorder)
     {
-      if (gtk_drag_check_threshold (GTK_WIDGET (self), 0, 0, offset_x, 0))
+      if (gtk_drag_check_threshold_double (GTK_WIDGET (self), 0, 0, offset_x, 0))
         {
           GtkColumnViewColumn *column;
           GtkWidget *header;
index 98b96c7594b73a13e5a69a8ad703cbb5ce154b7b..47f0ba179d430dee674ec31bfa7fe47b7a0bf524 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "config.h"
 
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 
 #include "gtkgesturedrag.h"
 #include "gtkgesturesingleprivate.h"
@@ -292,7 +292,7 @@ gtk_drag_source_update (GtkGesture       *gesture,
 
   widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
 
-  if (gtk_drag_check_threshold (widget, source->start_x, source->start_y, x, y))
+  if (gtk_drag_check_threshold_double (widget, source->start_x, source->start_y, x, y))
     {
       gtk_drag_source_drag_begin (source);
     }
@@ -796,3 +796,20 @@ gtk_drag_check_threshold (GtkWidget *widget,
   return (ABS (current_x - start_x) > drag_threshold ||
           ABS (current_y - start_y) > drag_threshold);
 }
+
+gboolean
+gtk_drag_check_threshold_double (GtkWidget *widget,
+                                 double     start_x,
+                                 double     start_y,
+                                 double     current_x,
+                                 double     current_y)
+{
+  int drag_threshold;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+  drag_threshold = gtk_settings_get_dnd_drag_threshold (gtk_widget_get_settings (widget));
+
+  return (ABS (current_x - start_x) > drag_threshold ||
+          ABS (current_y - start_y) > drag_threshold);
+}
diff --git a/gtk/gtkdragsourceprivate.h b/gtk/gtkdragsourceprivate.h
new file mode 100644 (file)
index 0000000..8226e3f
--- /dev/null
@@ -0,0 +1,33 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2020 Alexander Mikhaylenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_DRAG_SOURCE_PRIVATE_H__
+#define __GTK_DRAG_SOURCE_PRIVATE_H__
+
+#include "gtkdragsource.h"
+
+G_BEGIN_DECLS
+
+gboolean gtk_drag_check_threshold_double (GtkWidget *widget,
+                                          double     start_x,
+                                          double     start_y,
+                                          double     current_x,
+                                          double     current_y);
+
+G_END_DECLS
+
+#endif /* __GTK_DRAG_SOURCE_PRIVATE_H__ */
index 6a5d5cc6a1e7023eb8ad7fa0433037b10578be37..cbffee7ffd8ca9789be397b97b3e95d616c606a9 100644 (file)
@@ -65,7 +65,7 @@
 #include "gtkwindow.h"
 #include "gtknative.h"
 #include "gtkgestureclick.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkdragicon.h"
 #include "gtkwidgetpaintable.h"
 
@@ -1598,7 +1598,7 @@ icon_drag_update_cb (GtkGestureDrag *gesture,
   icon_info = priv->icons[pos];
 
   if (icon_info->content != NULL &&
-      gtk_drag_check_threshold (icon_info->widget, 0, 0, offset_x, offset_y))
+      gtk_drag_check_threshold_double (icon_info->widget, 0, 0, offset_x, offset_y))
     {
       GdkPaintable *paintable;
       GdkSurface *surface;
index 7a27a307fc4ba63207f24abd5b9b9385e66ee436..6bd92e380da8dc0039f1d99f2eec15881becbc58 100644 (file)
@@ -37,7 +37,7 @@
 #include "gtkgesturelongpressprivate.h"
 #include "gtkgestureprivate.h"
 #include "gtkmarshalers.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "gtkmarshalers.h"
@@ -159,7 +159,7 @@ gtk_gesture_long_press_update (GtkGesture       *gesture,
   priv = gtk_gesture_long_press_get_instance_private (GTK_GESTURE_LONG_PRESS (gesture));
   gtk_gesture_get_point (gesture, sequence, &x, &y);
 
-  if (gtk_drag_check_threshold (widget, priv->initial_x, priv->initial_y, x, y))
+  if (gtk_drag_check_threshold_double (widget, priv->initial_x, priv->initial_y, x, y))
     {
       if (priv->timeout_id)
         {
index 5d24c1f0bf9ea983d23c845af63b682764273e6e..cce12cc45de6954212db12ced5b4edc316eb2b72 100644 (file)
@@ -26,7 +26,7 @@
 #include "gtkcellrenderer.h"
 #include "gtkcellrendererpixbuf.h"
 #include "gtkcellrenderertext.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkentry.h"
 #include "gtkintl.h"
 #include "gtkmain.h"
@@ -5884,10 +5884,10 @@ gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view,
   if (icon_view->priv->pressed_button < 0)
     goto out;
 
-  if (!gtk_drag_check_threshold (GTK_WIDGET (icon_view),
-                                 icon_view->priv->press_start_x,
-                                 icon_view->priv->press_start_y,
-                                 x, y))
+  if (!gtk_drag_check_threshold_double (GTK_WIDGET (icon_view),
+                                        icon_view->priv->press_start_x,
+                                        icon_view->priv->press_start_y,
+                                        x, y))
     goto out;
 
   model = gtk_icon_view_get_model (icon_view);
index ef785bad04dff3cab7bacad6a9916a34750bc8ef..b5fa1358d108596a2f3ffcdfb0518a89282bddf9 100644 (file)
@@ -133,8 +133,8 @@ struct _GtkIconViewPrivate
   /* Drag-and-drop. */
   GdkModifierType start_button_mask;
   int pressed_button;
-  int press_start_x;
-  int press_start_y;
+  double press_start_x;
+  double press_start_y;
 
   GdkContentFormats *source_formats;
   GtkDropTargetAsync *dest;
index ad89d83a07aaf04cff6618b76ece6c55bb582d02..77264087ebf2a6d3957c1b6bce869b39d8e5667f 100644 (file)
@@ -21,6 +21,7 @@
 #include <string.h>
 #include <wayland-client-protocol.h>
 
+#include "gtk/gtkdragsourceprivate.h"
 #include "gtk/gtkimcontextwayland.h"
 #include "gtk/gtkintl.h"
 #include "gtk/gtkimmoduleprivate.h"
@@ -524,10 +525,10 @@ released_cb (GtkGestureClick     *gesture,
   if (global->focused &&
       n_press == 1 &&
       (hints & GTK_INPUT_HINT_INHIBIT_OSK) == 0 &&
-      !gtk_drag_check_threshold (context->widget,
-                                 context->press_x,
-                                 context->press_y,
-                                 x, y))
+      !gtk_drag_check_threshold_double (context->widget,
+                                        context->press_x,
+                                        context->press_y,
+                                        x, y))
     {
       zwp_text_input_v3_enable (global->text_input);
       g_signal_emit_by_name (global->current, "retrieve-surrounding", &result);
index 9e8fe679a7c740bcb946486b3f15661afba9cc9e..e6ca3a631c49453542145d59f59325aff8667130 100644 (file)
@@ -49,7 +49,7 @@
 #include "gtkwidgetprivate.h"
 #include "gtkpopovermenu.h"
 #include "gtknative.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkdragicon.h"
 #include "gtkcsscolorvalueprivate.h"
 
@@ -4273,7 +4273,7 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
 
   if (info->in_drag)
     {
-      if (gtk_drag_check_threshold (widget, info->drag_start_x, info->drag_start_y, x, y))
+      if (gtk_drag_check_threshold_double (widget, info->drag_start_x, info->drag_start_y, x, y))
         {
           GdkDrag *drag;
           GdkSurface *surface;
index ccb9ad23a5cb6ac0131640572ee1bbea368ebe56..53bf995c779fc1e12176403f385de5d4cc5bf3c7 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "gtkadjustment.h"
 #include "gtkbitset.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkdropcontrollermotion.h"
 #include "gtkgesturedrag.h"
 #include "gtkgizmoprivate.h"
@@ -1709,7 +1709,7 @@ gtk_list_base_drag_update (GtkGestureDrag *gesture,
 
   if (!priv->rubberband)
     {
-      if (!gtk_drag_check_threshold (GTK_WIDGET (self), 0, 0, offset_x, offset_y))
+      if (!gtk_drag_check_threshold_double (GTK_WIDGET (self), 0, 0, offset_x, offset_y))
         return;
       
       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
index ac1dbd88fec2295e21f1d17485466b50139dc2f4..ed712517b46055acf18e01d88d8a25c0c5b6e76a 100644 (file)
@@ -50,7 +50,7 @@
 #include "gtkstack.h"
 #include "gtktypebuiltins.h"
 #include "gtkwidgetprivate.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkwidgetpaintable.h"
 #include "gtknative.h"
 
@@ -248,14 +248,14 @@ struct _GtkNotebook
   GList         *first_tab;             /* The first tab visible (for scrolling notebooks) */
   GList         *focus_tab;
 
-  int            drag_begin_x;
-  int            drag_begin_y;
+  double         drag_begin_x;
+  double         drag_begin_y;
   int            drag_offset_x;
   int            drag_offset_y;
   int            drag_surface_x;
   int            drag_surface_y;
-  int            mouse_x;
-  int            mouse_y;
+  double         mouse_x;
+  double         mouse_y;
   int            pressed_button;
 
   GQuark         group;
@@ -2934,8 +2934,11 @@ gtk_notebook_motion (GtkEventController *controller,
 
   if (page->reorderable &&
       (notebook->operation == DRAG_OPERATION_REORDER ||
-       gtk_drag_check_threshold (widget, notebook->drag_begin_x, notebook->drag_begin_y,
-                                 notebook->mouse_x, notebook->mouse_y)))
+       gtk_drag_check_threshold_double (widget,
+                                        notebook->drag_begin_x,
+                                        notebook->drag_begin_y,
+                                        notebook->mouse_x,
+                                        notebook->mouse_y)))
     {
       GtkNotebookPointerPosition pointer_position = get_pointer_position (notebook);
 
index 41df790cc6b9c7e8008264987218ecd301c878b5..7c055296d369ebbf79b9596bebc9e0cf287d785b 100644 (file)
@@ -60,7 +60,7 @@
 #include "gtkgestureclick.h"
 #include "gtkgesturedrag.h"
 #include "gtknative.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkdragicon.h"
 
 /*< private >
@@ -3451,7 +3451,7 @@ on_row_dragged (GtkGestureDrag *gesture,
       return;
     }
 
-  if (gtk_drag_check_threshold (GTK_WIDGET (row), 0, 0, x, y))
+  if (gtk_drag_check_threshold_double (GTK_WIDGET (row), 0, 0, x, y))
     {
       double start_x, start_y;
       double drag_x, drag_y;
index 53136833c8723c5891a2de0d5af5d6bfcd81975a..e0454e108de0cd2b82a4260de4ad41931280883a 100644 (file)
@@ -29,7 +29,7 @@
 #include "gtkadjustment.h"
 #include "gtkadjustmentprivate.h"
 #include "gtkbuildable.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkeventcontrollermotion.h"
 #include "gtkeventcontrollerscroll.h"
 #include "gtkeventcontrollerprivate.h"
@@ -953,8 +953,8 @@ scrolled_window_drag_update_cb (GtkScrolledWindow *scrolled_window,
   GtkAdjustment *vadjustment;
   double dx, dy;
 
-  if (!gtk_drag_check_threshold (GTK_WIDGET (scrolled_window),
-                                 0, 0, offset_x, offset_y))
+  if (!gtk_drag_check_threshold_double (GTK_WIDGET (scrolled_window),
+                                        0, 0, offset_x, offset_y))
     return;
 
   gtk_scrolled_window_invalidate_overshoot (scrolled_window);
index c39e007d614343b7ec6023d27d351379c3d24d33..6d10722e6b89b6a2d9c4bd6b07ecdac6eef5aa03 100644 (file)
@@ -29,7 +29,7 @@
 #include "gtkbutton.h"
 #include "gtkdebug.h"
 #include "gtkdragicon.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkdroptarget.h"
 #include "gtkeditable.h"
 #include "gtkemojichooser.h"
@@ -2941,9 +2941,7 @@ gtk_text_drag_gesture_update (GtkGestureDrag *gesture,
   if (priv->in_drag)
     {
       if (gtk_text_get_display_mode (self) == DISPLAY_NORMAL &&
-          gtk_drag_check_threshold (widget,
-                                    priv->drag_start_x, priv->drag_start_y,
-                                    x, y))
+          gtk_drag_check_threshold_double (widget, 0, 0, offset_x, offset_y))
         {
           int *ranges;
           int n_ranges;
index 6f0bdb18bc2f962625708965723df7cd7e864dc2..bece4d58399702f8360f59aaa7238a9bb6077855 100644 (file)
@@ -32,6 +32,7 @@
 #include "gtkadjustmentprivate.h"
 #include "gtkcsscolorvalueprivate.h"
 #include "gtkdebug.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkdropcontrollermotion.h"
 #include "gtkintl.h"
 #include "gtkmain.h"
@@ -7232,7 +7233,7 @@ gtk_text_view_drag_gesture_update (GtkGestureDrag *gesture,
       /* If no data is attached, the initial press happened within the current
        * text selection, check for drag and drop to be initiated.
        */
-      if (gtk_drag_check_threshold (GTK_WIDGET (text_view), start_x, start_y, x, y))
+      if (gtk_drag_check_threshold_double (GTK_WIDGET (text_view), 0, 0, offset_x, offset_y))
         {
           if (!is_touchscreen)
             {
@@ -7366,7 +7367,7 @@ gtk_text_view_drag_gesture_end (GtkGestureDrag *gesture,
     gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN;
 
   if ((is_touchscreen || clicked_in_selection) &&
-      !gtk_drag_check_threshold (GTK_WIDGET (text_view), start_x, start_y, x, y))
+      !gtk_drag_check_threshold_double (GTK_WIDGET (text_view), 0, 0, offset_x, offset_y))
     {
       GtkTextIter iter;
 
index f6ed73b689c629df6efad3bbef8f6bdaf4fc7650..d1396e0d8a4480b9c1257a4f7557daabf96e980e 100644 (file)
@@ -28,7 +28,7 @@
 #include "gtkcellrenderer.h"
 #include "gtkcssnumbervalueprivate.h"
 #include "gtkcsscolorvalueprivate.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkdragicon.h"
 #include "gtkdroptargetasync.h"
 #include "gtkentryprivate.h"
@@ -7052,7 +7052,7 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view)
   gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture),
                                &offset_x, &offset_y);
 
-  if (!gtk_drag_check_threshold (widget, 0, 0, offset_x, offset_y))
+  if (!gtk_drag_check_threshold_double (widget, 0, 0, offset_x, offset_y))
     goto out;
 
   model = gtk_tree_view_get_model (tree_view);
index d546c994f275af7fc52afd1f7a3482cd77f97ac7..74060e916b5e85bac62fa00a9dc76a8c909bc219 100644 (file)
@@ -24,7 +24,7 @@
 #include "gtkcellareabox.h"
 #include "gtkcellareacontext.h"
 #include "gtkcelllayout.h"
-#include "gtkdragsource.h"
+#include "gtkdragsourceprivate.h"
 #include "gtkframe.h"
 #include "gtkimage.h"
 #include "gtkintl.h"
@@ -1072,7 +1072,7 @@ column_button_drag_update (GtkGestureDrag    *gesture,
 {
   GtkTreeViewColumnPrivate *priv = column->priv;
 
-  if (gtk_drag_check_threshold (priv->button, 0, 0, offset_x, offset_y))
+  if (gtk_drag_check_threshold_double (priv->button, 0, 0, offset_x, offset_y))
     {
       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
       _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (priv->tree_view), column,